package clw.toy;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;

/**
 * @author chengliwei
 */
public class NativeFunction implements Functionable {

    String methodName;

    Object instance;

    private static final Set<String> WHITE_LIST_METHOD_NAMES = Set.of("get", "remove");

    public NativeFunction(Object instance, String methodName) {
        this.instance = instance;
        this.methodName = methodName;
    }

    @Override
    public int arity() {
        return 0;
    }

    @Override
    public Object call(Interpreter interpreter, List<Object> arguments) {
        Class<?>[] argumentTypes = arguments.stream().map(o -> {
            if (o instanceof Integer && instanceNameAndArgumentTypeMatches()) {
                return int.class;
            }
            return Object.class;
        }).toArray(Class<?>[]::new);
        Object[] args = arguments.stream().toArray();
        try {
            Method method = instance.getClass().getDeclaredMethod(methodName, argumentTypes);
            Object res = method.invoke(instance, args);
            if (res instanceof Integer
                    || res instanceof Long
                    || res instanceof Double){
                return res;
            }
            return new NativeInstance(res);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    private boolean instanceNameAndArgumentTypeMatches() {
        if (instance instanceof ArrayList) {
            return WHITE_LIST_METHOD_NAMES.contains(methodName);
        }
        return false;
    }
}
